//------------------------------------------------------------------------------
//
//   author: 小兵（aosnow@yeah.net）
//   create: 2012-5-29 下午11:04:11
//    class: MediaCache - 运行时加载的资源缓存
//
//------------------------------------------------------------------------------

package starfire.rpc.loader
{
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.errors.IllegalOperationError;
	import flash.media.Sound;
	import flash.system.ApplicationDomain;
	import flash.system.System;
	import flash.utils.ByteArray;

	public class MediaCache
	{

		/**
		 * 加载资源数据的缓存
		 * <li>cache.image		- 图片资源（图片的 BitmapData）</li>
		 * <li>cache.swf		- SWF资源（资源包的 LoaderInfo）</li>
		 * <li>cache.binary		- 二进制资源（文件的 ByteArray）</li>
		 * <li>cache.audio		- 声音资源（声音文件的 Sound）</li>
		 * <li>cache.video		- VIDEO数据（视频文件的 ByteArray）</li>
		 * <li>cache.text		- 纯文本字符串数据</li>
		 * <li>cache.xml		- XML数据</li>
		 */
		protected static var cache:Object = {};

		/**
		 * 检测缓存区是否已经有资源存在
		 * @param type					- 资源类型
		 * @param name					- 资源名称
		 * @return
		 */
		public static function hasResource( type:String, name:String ):Boolean
		{
			var resContainer:Vector.<ContentInfo>;

			if( cache.hasOwnProperty( type ))
			{
				resContainer = cache[ type ] as Vector.<ContentInfo>;
			}
			else
			{
				return false;
			}

			for each( var content:ContentInfo in resContainer )
			{
				if( content.name == name && content.type == type )
					return true;
			}

			return false;
		}

		/**
		 * 缓存数据
		 * @param type					- 数据类型
		 * @param name					- 数据名称
		 * @param data					- 数据源
		 */
		public static function cacheResource( message:LoadMessage, data:Object ):void
		{
			var resContainer:Vector.<ContentInfo>;

			if( cache.hasOwnProperty( message.mediaType ))
			{
				resContainer = cache[ message.mediaType ] as Vector.<ContentInfo>;
			}
			else
			{
				resContainer = new Vector.<ContentInfo>();
				cache[ message.mediaType ] = resContainer;
			}

			var content:ContentInfo = new ContentInfo();
			content.type = message.mediaType;
			content.name = message.name;
			content.url = message.url;
			content.group = message.group;
			content.content = data;

			resContainer[ resContainer.length ] = content;
		}

		/**
		 * 直接缓存位图数据，以免重复创建
		 * @param value
		 */
		public static function cacheBitmapData( name:String, value:BitmapData ):void
		{
			var resContainer:Vector.<ContentInfo>;
			var mediaType:String = MediaType.IMAGE;

			if( cache.hasOwnProperty( mediaType ))
			{
				resContainer = cache[ mediaType ] as Vector.<ContentInfo>;
			}
			else
			{
				resContainer = new Vector.<ContentInfo>();
				cache[ mediaType ] = resContainer;
			}

			var content:ContentInfo = new ContentInfo();
			content.type = mediaType;
			content.name = name;
			content.group = "bitmapdata";
			content.content = value;

			resContainer[ resContainer.length ] = content;
		}

		/**
		 * 缓存字节数据
		 * <p>当加载SWF时，会以字节数据加载，加载完成时会同时保存字节源数据，以及从字节源数据中加载的SWF对象。
		 * 所保存的字节源数据，可以达到拷贝SWF的目的，只要再次从字节源数据中加载即可。
		 * </p>
		 * @param type					- 数据类型
		 * @param name					- 数据名称
		 * @param data					- 数据源
		 */
		public static function cacheByteArray( message:LoadMessage, data:ByteArray ):void
		{
			var resContainer:Vector.<ContentInfo>;

			if( cache.hasOwnProperty( MediaType.BINARY ))
			{
				resContainer = cache[ MediaType.BINARY ] as Vector.<ContentInfo>;
			}
			else
			{
				resContainer = new Vector.<ContentInfo>();
				cache[ MediaType.BINARY ] = resContainer;
			}

			var content:ContentInfo = new ContentInfo();
			content.type = MediaType.BINARY;
			content.name = message.name;
			content.url = message.url;
			content.group = message.group;
			content.content = data;

			resContainer[ resContainer.length ] = content;
		}

		/**
		 * 清除已经加载的现有资源，释放所占用内存
		 * @param type					- 资源类型
		 * @param name					- 资源名称
		 * @return 若资源存在并删除成功则返回 true，否则返回 false
		 */
		public static function deleteContent( type:String, name:String ):Boolean
		{
			var resContainer:Vector.<ContentInfo>;

			if( cache.hasOwnProperty( type ))
			{
				resContainer = cache[ type ] as Vector.<ContentInfo>;
			}
			else
			{
				return false;
			}

			for each( var content:ContentInfo in resContainer )
			{
				if( content.name == name && content.type == type )
				{
					destroyContent( content, resContainer );
					return true;
				}
			}

			return false;
		}

		//--------------------------------------------------------------------------
		//
		//   数据缓存访问
		//
		//--------------------------------------------------------------------------

		public static function cloneByteArray( bytes:ByteArray ):ByteArray
		{
			var clone:ByteArray = new ByteArray();

			if( bytes && ( bytes.bytesAvailable > 0 || bytes.length > 0 ))
			{
				bytes.position = 0;
				clone.position = 0;
				clone.writeBytes( bytes, 0, bytes.length );
			}

			return clone;
		}

		/**
		 * 以加载任务的描述信息来尝试获取缓存的加载资源数据
		 * @param message 任务描述信息
		 * @return
		 */
		public static function getDataByMessage( message:LoadMessage ):*
		{
			switch( message.mediaType )
			{
				case MediaType.IMAGE:
					message.result = getBitmapData( message.name );
					return message;
				case MediaType.SWF:
					return getLoaderInfo( message.name );
				case MediaType.BINARY:
					return getBinary( message.name );
				case MediaType.VIDEO:
					return null;
				case MediaType.AUDIO:
					return getSoundAsset( message.name );
				case MediaType.XML:
					return getXML( message.name );
				case MediaType.TEXT:
					return getText( message.name );
				default:
					throw new IllegalOperationError( "ERROR: 非法的资源类型！" );
			}
		}

		/**
		 * 根据资源类型和加载名称取得对应的内容对象
		 * @param type 资源类型
		 * @param name 资源名称
		 * @return
		 */
		public static function getContent( type:String, name:String ):ContentInfo
		{
			if( cache.hasOwnProperty( type ))
			{
				var resContainer:Vector.<ContentInfo> = cache[ type ] as Vector.<ContentInfo>;

				for each( var content:ContentInfo in resContainer )
				{
					if( content.name == name )
					{
						return content;
					}
				}
			}

			return null;
		}

		/**
		 * 释放所有作为临时加载的内容资源
		 * <p>通过此方法来释放所有的临时资源，以减少内在占用量，更好的保证游戏运行效率。
		 * 例如，进入一张新地图时，一般把所有该地图相关的资源作为临时内容加载，而后进行使用。
		 * 当退出这张地图时，同时通过该方法释放临时资源内存。
		 * </p>
		 *
		 * @see starfire.rpc.loader.LoadMessage
		 */
		public static function destroyGroup( group:String = "temp" ):void
		{
			var types:Array = [ MediaType.IMAGE,
								MediaType.SWF,
								MediaType.BINARY,
								MediaType.VIDEO,
								MediaType.AUDIO,
								MediaType.XML,
								MediaType.TEXT ];

			var resContainer:Vector.<ContentInfo>;

			for each( var type:String in types )
			{
				if( cache.hasOwnProperty( type ))
				{
					resContainer = cache[ type ] as Vector.<ContentInfo>;

					for each( var content:ContentInfo in resContainer )
					{
						if( content.group == group )
						{
							destroyContent( content, resContainer );
						}
					}
				}
			}
		}

		protected static function destroyContent( content:ContentInfo, resContainer:Vector.<ContentInfo> ):void
		{
			// 从资源列表中删除引用
			resContainer.splice( resContainer.indexOf( content ), 1 );

			// 释放资源内存
			switch( content.type )
			{
				case MediaType.IMAGE:
				{
					( content.content as BitmapData ).dispose();
					break;
				}
				case MediaType.SWF:
				{
					( content.content as Loader ).unloadAndStop( true );
					break;
				}
				case MediaType.BINARY:
				{
					( content.content as ByteArray ).clear();
					break;
				}
				case MediaType.AUDIO:
				{
					break;
				}
				case MediaType.XML:
				{
					System.disposeXML( content.content as XML );
					break;
				}
				case MediaType.TEXT:
				{
					break;
				}
			}

			content.content = null;
		}

		/**
		 * 从加载的资源数据缓存中提取图片数据
		 * @param name					- 加载资源时赋予的 name，必须相对应才能正确取得图片数据
		 * @return 若位图资源存在，则返回 <code>BitmapData</code>，否则返回 <code>null</code>
		 */
		public static function getBitmapData( name:String ):BitmapData
		{
			var content:ContentInfo = getContent( MediaType.IMAGE, name );

			if( content )
			{
				return content.content as BitmapData;
			}

			return null;
		}

		/**
		 * 从加载的资源数据缓存中提取文件字节数据
		 * @param name					- 加载资源时赋予的 name，必须相对应才能正确取得文件数据
		 * @return 若字节数组对象存在，则返回 <code>ByteArray</code>，否则返回 <code>null</code>
		 */
		public static function getBinary( name:String ):ByteArray
		{
			var content:ContentInfo = getContent( MediaType.BINARY, name );

			if( content )
			{
				return cloneByteArray( content.content as ByteArray );
			}

			return null;
		}

		/**
		 * 从加载的资源数据缓存中提取XML数据
		 * @param name					- 加载资源时赋予的 name，必须相对应才能正确取得数据
		 * @return 若字XML对象存在，则返回 <code>XML</code>，否则返回 <code>null</code>
		 */
		public static function getXML( name:String ):XML
		{
			var content:ContentInfo = getContent( MediaType.XML, name );

			if( content )
			{
				return content.content as XML;
			}

			return null;
		}

		/**
		 * 从加载的资源数据缓存中提取Text字符串数据
		 * @param name					- 加载资源时赋予的 name，必须相对应才能正确取得文本数据
		 * @return 返回对应的文本字符串内容，若不存在数据则返回 <code>null</code>
		 */
		public static function getText( name:String ):String
		{
			var content:ContentInfo = getContent( MediaType.TEXT, name );

			if( content )
			{
				return content.content as String;
			}

			return null;
		}

		/**
		 * 从加载的资源数据缓存中提取声音媒体数据定义
		 * @param name					- 加载资源时赋予的 name，必须相对应才能正确取得声音媒体数据
		 * @return 若声音资源存在，则返回这个 <code>Sound</code> 对象，否则返回 <code>null</code>
		 */
		public static function getSoundAsset( name:String ):Sound
		{
			var content:ContentInfo = getContent( MediaType.AUDIO, name );

			if( content )
			{
				return content.content as Sound;
			}

			return null;
		}

		/**
		 * 从加载的资源数据缓存中提取加载的内容对象
		 * @param name					- 加载资源时赋予的 name，必须相对应才能正确取得内容对象
		 * @return 若内容对象存在，则返回这个 <code>LoaderInfo</code> 对象，否则返回 <code>null</code>
		 */
		public static function getLoaderInfo( name:String ):LoaderInfo
		{
			var content:ContentInfo = getContent( MediaType.SWF, name );

			if( content )
			{
				return ( content.content as Loader ).contentLoaderInfo;
			}

			return null;
		}

		/**
		 * 从加载的SWF字节源数据中读取Swf内容
		 * <p>必须是所加载的字节源数据来源于加载SWF，否则失败</p>
		 * @param name					- 加载资源时赋予的 name，必须相对应才能正确取得内容对象
		 * @return 若内容对象存在，则返回这个 <code>Loader</code> 对象，否则返回 <code>null</code>
		 */
		public static function getLoaderInByteArray( name:String ):Loader
		{
			var content:ContentInfo = getContent( MediaType.BINARY, name );
			var loader:Loader = new Loader();

			if( content )
			{
				loader.loadBytes( content.content );
			}

			return loader;
		}

		/**
		 * 从加载的SWF资源域中提取指定的类定义
		 * @param className				- 类定义的完整名称（包含路径和类名）
		 * @param name					- 加载资源时赋予的 name，必须相对应才能正确取得应用程序域。若为 null，则从当前程序域中查找
		 * @return 若类存在，则返回这个类对象，否则返回 <code>null</code>
		 */
		public static function getClass( className:String, name:String = null ):Class
		{
			var domain:ApplicationDomain = ApplicationDomain.currentDomain;

			if( name )
			{
				domain = getLoaderInfo( name ).applicationDomain;
			}

			if( domain.hasDefinition( className ))
			{
				return Class( domain.getDefinition( className ));
			}

			return null;
		}
	}
}
